﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using Styx.Database;
using Styx.Helpers;
using Styx.Logic.BehaviorTree;
using Styx.Logic.Inventory.Frames.Gossip;
using Styx.Logic.Pathing;
using Styx.Logic.Questing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using TreeSharp;
using Action = TreeSharp.Action;

namespace Styx.Bot.Quest_Behaviors
{
    /// <summary>
    /// InteractWith_Name by AxaZol
    /// Allows you to do quests that requires you to interact with nearby objects by name.
    /// ##Syntax##
    /// QuestId: Id of the quest.
    /// TargetName: Id of the object to interact with.
    /// NumOfTimes: Number of times to interact with object.
    /// [Optional]CollectionDistance: The distance it will use to collect objects. DefaultValue:100 yards
	/// [Optional]WaitTime: Time to wait after interact with object. DefaultValue: 1500 ms
    /// TargetType: the type of object to interact with, expected value: Npc/Gameobject
    /// X,Y,Z: The general location where theese objects can be found
    /// </summary>
    public class InteractWith_Name : CustomForcedBehavior
    {
        public InteractWith_Name(Dictionary<string, string> args)
            : base(args)
        {
            bool error = false;

            uint questId;
            if (!uint.TryParse(Args["QuestId"], out questId))
            {
                Logging.Write("Parsing attribute 'QuestId' in InteractWith_Name behavior failed! please check your profile!");
                error = true;
            }
			
			if (!Args.ContainsKey("TargetName"))
            {
                Logging.Write("Could not find attribute 'TargetName' in InteractWith_Name behavior! please check your profile!");
                error = true;
            }
			
            string targetName = Args["TargetName"];

            int numOfTimes;
            if (!int.TryParse(Args["NumOfTimes"], out numOfTimes))
            {
                Logging.Write("Parsing attribute 'NumOfTimes' in InteractWith_Name behavior failed! please check your profile!");
                error = true;
            }

            if (Args.ContainsKey("CollectionDistance"))
            {
                int distance;
                int.TryParse(Args["CollectionDistance"], out distance);
                CollectionDistance = distance != 0 ? distance : 100;
            }
			
			if(Args.ContainsKey("WaitTime"))
            {
                int waitTime;
                int.TryParse(Args["WaitTime"], out waitTime);
                WaitTime = waitTime != 0 ? waitTime : 1500;
            }

            if (!Args.ContainsKey("TargetType"))
            {
                Logging.Write("Could not find attribute 'TargetType' in InteractWith_Name behavior! please check your profile!");
                error = true;
            }

            var type = (TargetType)Enum.Parse(typeof(TargetType), Args["TargetType"], true);

            float x, y, z;
            if (!float.TryParse((string)Args["X"], out x))
            {
                Logging.Write("Parsing attribute 'X' in InteractWith_Name behavior failed! please check your profile!");
                error = true;
            }

            if (!float.TryParse((string)Args["Y"], out y))
            {
                Logging.Write("Parsing attribute 'Y' in InteractWith_Name behavior failed! please check your profile!");
                error = true;
            }

            if (!float.TryParse((string)Args["Z"], out z))
            {
                Logging.Write("Parsing attribute 'Z' in InteractWith_Name behavior failed! please check your profile!");
                error = true;
            }

            if (error)
                Thread.CurrentThread.Abort();

            TargetType = type;
            QuestId = questId;
            NumOfTimes = numOfTimes;
            TargetName = targetName;
            Location = new WoWPoint(x, y, z);
        }

        public WoWPoint Location { get; private set; }
        public int WaitTime { get; private set; }
        public int Counter { get; set; }
        public string TargetName { get; set; }
        public int NumOfTimes { get; set; }
        public uint QuestId { get; private set; }
        public TargetType TargetType { get; private set; }
        public int CollectionDistance = 100;

        private readonly List<ulong> _npcBlacklist = new List<ulong>();

        /// <summary> Current object we should interact with.</summary>
        /// <value> The object.</value>
        private WoWObject CurrentObject
        {
            get
            {
                WoWObject @object = null;
                switch (TargetType)
                {
                    case TargetType.Gameobject:
                        @object = ObjectManager.GetObjectsOfType<WoWGameObject>().OrderBy(ret => ret.Distance).FirstOrDefault(obj =>
                            !_npcBlacklist.Contains(obj.Guid) &&
                            obj.Distance < CollectionDistance &&
                            obj.Name == TargetName);

                        break;

                    case TargetType.Npc:
                        @object = ObjectManager.GetObjectsOfType<WoWUnit>().OrderBy(ret => ret.Distance).FirstOrDefault(obj =>
                            !_npcBlacklist.Contains(obj.Guid) &&
                            obj.Distance < CollectionDistance &&
                            obj.Name == TargetName);

                        break;

                }

                if (@object != null)
                {
                    Logging.Write(@object.Name);
                }
                return @object;
            }
        }

        #region Overrides of CustomForcedBehavior

        private Composite _root;
        protected override Composite CreateBehavior()
        {
            return _root ?? (_root =
                new PrioritySelector(

                    new Decorator(ret => Counter >= NumOfTimes,
                        new Action(ret => _isDone = true)),

                        new PrioritySelector(

                            new Decorator(ret => CurrentObject != null && !CurrentObject.WithinInteractRange,
                                new Sequence(
                                    new Action(delegate { TreeRoot.StatusText = "Moving to interact with - " + CurrentObject.Name; }),
                                    new Action(ret => Navigator.MoveTo(CurrentObject.Location))
                                    )
                                ),
                                
                            new Decorator(ret => CurrentObject != null && CurrentObject.WithinInteractRange,
                                new Sequence(
                                    new DecoratorContinue(ret => StyxWoW.Me.IsMoving,
                                        new Action(delegate
                                            {
                                                WoWMovement.MoveStop();
                                                StyxWoW.SleepForLagDuration();
                                            })),

                                        new Action(delegate
                                        {
                                            TreeRoot.StatusText = "Interacting with - " + CurrentObject.Name;
                                            CurrentObject.Interact();
                                            _npcBlacklist.Add(CurrentObject.Guid);

                                            StyxWoW.SleepForLagDuration();
                                            Counter++;
                                            Thread.Sleep(WaitTime);
                                        }))
                                        ),

                            new Sequence(
                                new Action(delegate { TreeRoot.StatusText = "Moving towards - " + Location; }),
                                new Action(ret => Navigator.MoveTo(Location))))
                    ));
        }

        private bool _isDone;
        public override bool IsDone
        {
            get
            {
                PlayerQuest quest = StyxWoW.Me.QuestLog.GetQuestById(QuestId);

                return
                    _isDone ||
                    (quest != null && quest.IsCompleted) ||
                    quest == null;
            }
        }

        public override void OnStart()
        {
            PlayerQuest quest = StyxWoW.Me.QuestLog.GetQuestById(QuestId);
            if (quest != null)
                TreeRoot.GoalText = string.Format("Interacting with: {0} {1} Times for quest:{2}", TargetName, NumOfTimes, quest.Name);
        }

        #endregion
    }

    public enum TargetType
    {
        Npc,
        Gameobject
    }
}
